﻿namespace PlugNT.Safe
{
    using System;
    using System.IO;
    using System.Security.Cryptography;
    using System.Text;
    using System.Web.Security;

    public class Encrypter
    {
        private const int BUFFER_SIZE = 0x20000;
        private const string DESKey = "agui-2009-09-25";
        private const ulong FC_TAG = 18158850165396952783L;
        private static RandomNumberGenerator rand = new RNGCryptoServiceProvider();

        private static bool CheckByteArrays(byte[] b1, byte[] b2)
        {
            if (b1.Length == b2.Length)
            {
                for (int i = 0; i < b1.Length; i++)
                {
                    if (b1[i] != b2[i])
                    {
                        return false;
                    }
                }
                return true;
            }
            return false;
        }

        private static SymmetricAlgorithm CreateRijndael(string password, byte[] salt)
        {
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, salt, "SHA256", 0x3e8);
            SymmetricAlgorithm sma = Rijndael.Create();
            sma.KeySize = 0x100;
            sma.Key = pdb.GetBytes(0x20);
            sma.Padding = PaddingMode.PKCS7;
            return sma;
        }

        public static void DecryptFile(string inFile, string outFile, string password)
        {
            using (FileStream fin = File.OpenRead(inFile))
            {
                using (FileStream fout = File.OpenWrite(outFile))
                {
                    int size = (int) fin.Length;
                    byte[] bytes = new byte[0x20000];
                    int read = -1;
                    int value = 0;
                    int outValue = 0;
                    byte[] IV = new byte[0x10];
                    fin.Read(IV, 0, 0x10);
                    byte[] salt = new byte[0x10];
                    fin.Read(salt, 0, 0x10);
                    SymmetricAlgorithm sma = CreateRijndael(password, salt);
                    sma.IV = IV;
                    value = 0x20;
                    long lSize = -1;
                    HashAlgorithm hasher = SHA256.Create();
                    using (CryptoStream cin = new CryptoStream(fin, sma.CreateDecryptor(), CryptoStreamMode.Read))
                    {
                        using (CryptoStream chash = new CryptoStream(Stream.Null, hasher, CryptoStreamMode.Write))
                        {
                            BinaryReader br = new BinaryReader(cin);
                            lSize = br.ReadInt64();
                            ulong tag = br.ReadUInt64();
                            if (18158850165396952783L != tag)
                            {
                                throw new CryptoHelpException("文件被破坏");
                            }
                            long numReads = lSize / 0x20000;
                            long slack = lSize % 0x20000;
                            for (int i = 0; i < numReads; i++)
                            {
                                read = cin.Read(bytes, 0, bytes.Length);
                                fout.Write(bytes, 0, read);
                                chash.Write(bytes, 0, read);
                                value += read;
                                outValue += read;
                            }
                            if (slack > 0)
                            {
                                read = cin.Read(bytes, 0, (int) slack);
                                fout.Write(bytes, 0, read);
                                chash.Write(bytes, 0, read);
                                value += read;
                                outValue += read;
                            }
                            chash.Flush();
                            chash.Close();
                            fout.Flush();
                            fout.Close();
                            byte[] curHash = hasher.Hash;
                            byte[] oldHash = new byte[hasher.HashSize / 8];
                            read = cin.Read(oldHash, 0, oldHash.Length);
                            if (!((oldHash.Length == read) && CheckByteArrays(oldHash, curHash)))
                            {
                                throw new CryptoHelpException("文件被破坏");
                            }
                        }
                    }
                    if (outValue != lSize)
                    {
                        throw new CryptoHelpException("文件大小不匹配");
                    }
                }
            }
        }

        public static string DESDecrypt(string Text)
        {
            return DESDecrypt(Text, "agui-2009-09-25");
        }

        public static string DESDecrypt(string Text, string sKey)
        {
            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            int len = Text.Length / 2;
            byte[] inputByteArray = new byte[len];
            for (int x = 0; x < len; x++)
            {
                int i = Convert.ToInt32(Text.Substring(x * 2, 2), 0x10);
                inputByteArray[x] = (byte) i;
            }
            des.Key = Encoding.ASCII.GetBytes(FormsAuthentication.HashPasswordForStoringInConfigFile(sKey, "md5").Substring(0, 8));
            des.IV = Encoding.ASCII.GetBytes(FormsAuthentication.HashPasswordForStoringInConfigFile(sKey, "md5").Substring(0, 8));
            MemoryStream ms = new MemoryStream();
            CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write);
            cs.Write(inputByteArray, 0, inputByteArray.Length);
            cs.FlushFinalBlock();
            return Encoding.Default.GetString(ms.ToArray());
        }

        public static string DESEncrypt(string Text)
        {
            return DESEncrypt(Text, "agui-2009-09-25");
        }

        public static string DESEncrypt(string Text, string sKey)
        {
            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            byte[] inputByteArray = Encoding.Default.GetBytes(Text);
            des.Key = Encoding.ASCII.GetBytes(FormsAuthentication.HashPasswordForStoringInConfigFile(sKey, "md5").Substring(0, 8));
            des.IV = Encoding.ASCII.GetBytes(FormsAuthentication.HashPasswordForStoringInConfigFile(sKey, "md5").Substring(0, 8));
            MemoryStream ms = new MemoryStream();
            CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
            cs.Write(inputByteArray, 0, inputByteArray.Length);
            cs.FlushFinalBlock();
            StringBuilder ret = new StringBuilder();
            foreach (byte b in ms.ToArray())
            {
                ret.AppendFormat("{0:X2}", b);
            }
            return ret.ToString();
        }

        public static void EncryptFile(string inFile, string outFile, string password)
        {
            using (FileStream fin = File.OpenRead(inFile))
            {
                using (FileStream fout = File.OpenWrite(outFile))
                {
                    long lSize = fin.Length;
                    int size = (int) lSize;
                    byte[] bytes = new byte[0x20000];
                    int read = -1;
                    int value = 0;
                    byte[] IV = GenerateRandomBytes(0x10);
                    byte[] salt = GenerateRandomBytes(0x10);
                    SymmetricAlgorithm sma = CreateRijndael(password, salt);
                    sma.IV = IV;
                    fout.Write(IV, 0, IV.Length);
                    fout.Write(salt, 0, salt.Length);
                    HashAlgorithm hasher = SHA256.Create();
                    using (CryptoStream cout = new CryptoStream(fout, sma.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        using (CryptoStream chash = new CryptoStream(Stream.Null, hasher, CryptoStreamMode.Write))
                        {
                            BinaryWriter bw = new BinaryWriter(cout);
                            bw.Write(lSize);
                            bw.Write((ulong) 18158850165396952783L);
                            while ((read = fin.Read(bytes, 0, bytes.Length)) != 0)
                            {
                                cout.Write(bytes, 0, read);
                                chash.Write(bytes, 0, read);
                                value += read;
                            }
                            chash.Flush();
                            chash.Close();
                            byte[] hash = hasher.Hash;
                            cout.Write(hash, 0, hash.Length);
                            cout.Flush();
                            cout.Close();
                        }
                    }
                }
            }
        }

        private static byte[] GenerateRandomBytes(int count)
        {
            byte[] bytes = new byte[count];
            rand.GetBytes(bytes);
            return bytes;
        }

        public static string MD5(string hashText)
        {
            return FormsAuthentication.HashPasswordForStoringInConfigFile(hashText, "MD5").ToLower();
        }

        public static string MD5(Encoding encode, string hashText)
        {
            byte[] b = encode.GetBytes(hashText);
            b = new MD5CryptoServiceProvider().ComputeHash(b);
            StringBuilder ret = new StringBuilder();
            for (int i = 0; i < b.Length; i++)
            {
                ret.Append(b[i].ToString("x").PadLeft(2, '0'));
            }
            return ret.ToString();
        }

        public static string MD5Hasher(string hashText)
        {
            byte[] MD5Data = Encoding.UTF8.GetBytes(hashText);
            System.Security.Cryptography.MD5 Md5 = new MD5CryptoServiceProvider();
            return Convert.ToBase64String(Md5.ComputeHash(MD5Data));
        }
    }
}

